/**********************************************************************************
 * Code license:  GNU General Public License v3                                   *
 * Main page:     https://code.google.com/p/library-cross-platform/               *
 * Issue tracker: https://code.google.com/p/library-cross-platform/issues/list    *
***********************************************************************************/

package library.ui;

import javax.swing.*;

import java.awt.*;
import java.awt.event.*;

import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.text.*;

import java.util.*;
import library.util.resources.*;

@SuppressWarnings({"serial"})
public class DialogBuilder extends JDialog implements ActionListener,
                                                      DocumentListener,
                                                      UpdateElementListener
{
  protected static final String              ButtonPanelName         = new String("DialogBuilder.Button.Panel");
  protected static final String              clientPropertyElementID = new String("Property.ElementID");

  protected              TabContainerElement tabContainerModel       = null;
  protected              ElementListener     elementListener         = null;

  public DialogBuilder(Frame parent, TabContainerElement model) {
   super(parent, model.getName(), true);
   JComponent mainPanel   = createFrameWindow(model);
   JPanel     buttonPanel = createButtonPanel();
   Container  contentPane = getContentPane();

   this.setTitle(model.getName());
   tabContainerModel = model;
   
   contentPane.add(mainPanel, BorderLayout.CENTER);
   contentPane.add(buttonPanel, BorderLayout.PAGE_END);
   pack();
   setLocationRelativeTo(parent);   
  }
  
  public void setElementListener(ElementListener e) {
    elementListener = e;
  }
  
  public ElementContainer getRootElementContainer() {
    return tabContainerModel;
  }
  
  protected class FlowLayoutLocal extends FlowLayout {
    public FlowLayoutLocal(int align) {
      super(align);
    }
    
    public Dimension preferredLayoutSize(Container target) {
      synchronized (target.getTreeLock()) {
        Dimension dim                   = new Dimension(0, 0);
        int       nmembers              = target.getComponentCount();
        boolean   firstVisibleComponent = true;
        int       max_height            = 0;

        for (int i = 0 ; i < nmembers ; i++) {
          Component m = target.getComponent(i);
          if (m.isVisible()) {
            Dimension d = m.getPreferredSize();
            dim.height = Math.max(dim.height, d.height);
            max_height = (dim.height > max_height) ? dim.height : max_height;
            if (firstVisibleComponent) {
              firstVisibleComponent = false;
            } else {
              dim.width += this.getHgap();
            }
            dim.width += d.width;
          }
        }
      
        for(int i = 0; i < nmembers; i ++) {
          Component m = target.getComponent(i);
          if(m instanceof JSeparator) {
            Dimension localdim = m.getPreferredSize();
            localdim.height = max_height;
            localdim.width  = 5;
            m.setPreferredSize(localdim);
          }
        }
      
        Insets insets = target.getInsets();
        dim.width += insets.left + insets.right + this.getHgap()*2;
        dim.height += insets.top + insets.bottom + this.getVgap()*2;
        return dim;
      }
    }
  }
  
  
  protected JComponent createFrameWindow(TabContainerElement model) {
    JTabbedPane      mainPanel = new JTabbedPane();
    int              count_max = model.size();
    
    for(int icount = 0; icount < count_max; icount ++) {
      ElementContainer econtainer = (ElementContainer)model.get(icount);
      JPanel           epanel     = createElementPanel(econtainer);
      mainPanel.addTab(econtainer.toString(), new JScrollPane(epanel));
    }
    return mainPanel;
  }
  
  protected JPanel createButtonPanel() {
    JPanel buttonPanel     = new JPanel();
    JButton okButton       = new JButton(StringResources.OK);
    JButton cancelButton   = new JButton(StringResources.CANCEL);
    
    okButton.addActionListener(new OKButtonActionListener());
    cancelButton.addActionListener(new CancelButtonActionListener());
    
    buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
    buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
    buttonPanel.add(Box.createHorizontalGlue());
    buttonPanel.add(cancelButton);
    buttonPanel.add(Box.createRigidArea(new Dimension(10,10)));
    buttonPanel.add(okButton);
    buttonPanel.setName(ButtonPanelName);
    return buttonPanel;
  }
  
  protected JPanel createElementPanel(ElementContainer container) {
    int        max_width    = getMaxWidthForTitleColumn(container);
    int        count_max    = container.size();
    JPanel     pane         = new JPanel();
    Dimension  dim          = new Dimension(max_width, 0);
    Element    element      = null;
    JComponent jelement     = null;
    JPanel     elementPane  = null;
    JLabel     title        = null;
    
    pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
    
    for(int icount = 0; icount < count_max; icount ++) {
      element = container.get(icount);
/** create title pane */
      elementPane = new JPanel();
      title       = new JLabel(element.toString());
      title.setMinimumSize(dim);
      title.setPreferredSize(dim);
      element.setName(""); //reset border name
      elementPane.setLayout(new BoxLayout(elementPane, BoxLayout.X_AXIS));
      
      jelement = createElement(element);
      if(jelement != null) {
        jelement.setLayout(new FlowLayoutLocal(FlowLayout.LEFT));
        jelement.putClientProperty(clientPropertyElementID, new Long(element.getElementID()));
        elementPane.add(title);
        elementPane.add(jelement);
        pane.add(elementPane);
      }
    }
    return pane;
  }
  
  protected JComponent createElement(Element element) {
    JComponent retComponent = null;
    switch(element.getElementType()) {
    case Element.CHECKBOX_TYPE:
      retComponent = getCheckBox((CheckBoxElement)element);
      break;
    case Element.ELEMENT_CONTAINER_TYPE:
      retComponent = getElementContainer((ElementContainer)element);
      break;
    case Element.RADIOBUTTON_TYPE:
      retComponent = getRadioButton((RadioButtonElement)element);
      break;
    case Element.RADIOBUTTON_CONTAINER_TYPE:
      retComponent = getRadioButtonContainer((RadioButtonContainer)element);
      break;
    case Element.TEXTLABEL_TYPE:
      retComponent = getLabel((LabelElement)element);
      break;
    case Element.TEXTEDIT_TYPE:
      retComponent = getText((TextElement)element);
      break;
    case Element.SEPARATOR_TYPE:
      retComponent = getSeparator((SeparatorElement)element);
      break;
    default:
      retComponent = null;
      break;
    }
    return retComponent;
  }
  
  protected JPanel getElementContainer(ElementContainer element) {
    JPanel     retcomponent = new JPanel();
    JComponent jelement     = null;
    Border     border       = null;
    
    if(element.isShowBorder() == true) {
      if(element.toString() == null || element.toString().length() == 0)
        border = BorderFactory.createLineBorder(Color.GRAY);
      else
        border = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.GRAY),
                                                  element.toString());
    }
    retcomponent.setBorder(border);
    
    for(int ielement = 0; ielement < element.size(); ielement ++) {
      jelement = createElement(element.get(ielement));
      if(jelement != null) {
        jelement.putClientProperty(clientPropertyElementID, new Long(element.get(ielement).getElementID()));
        retcomponent.add(jelement);
      }
    }
    return retcomponent;
  }
  
  protected JCheckBox getCheckBox(CheckBoxElement element) {
   JCheckBox retcomponent = new JCheckBox(element.getText(), element.getState());
   retcomponent.addActionListener(this);
   retcomponent.setHorizontalTextPosition(JCheckBox.CENTER);
   retcomponent.setVerticalTextPosition(JCheckBox.TOP);
   retcomponent.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
   return retcomponent;
  }
  
  protected JRadioButton getRadioButton(RadioButtonElement element) {
    JRadioButton retcomponent = new JRadioButton(element.getText(), element.getState());
    retcomponent.addActionListener(this);
    retcomponent.setHorizontalTextPosition(JRadioButton.CENTER);
    retcomponent.setVerticalTextPosition(JRadioButton.TOP);
    retcomponent.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
    return retcomponent;
  }
  
  protected JLabel getLabel(LabelElement element) {
    JLabel retcomponent = new JLabel(element.getText());
    return retcomponent;
  }
  
  protected JComponent getText(TextElement element) {
    JTextArea   textcomponent = new JTextArea(element.getText(),
                                              element.getSymbolCountH(),
                                              element.getSymbolCountW());
    textcomponent.getDocument().addDocumentListener(this);
    textcomponent.getDocument().putProperty(clientPropertyElementID, new Long(element.getElementID()));
    JScrollPane retcomponent  = new JScrollPane(textcomponent);
    retcomponent.setVerticalScrollBarPolicy((element.getVerticalScrollBar() == true) ?
                                             ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS :
                                             ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
    retcomponent.setHorizontalScrollBarPolicy((element.getVerticalScrollBar() == true) ?
                                              ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS :
                                              ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    return retcomponent;
  }
  
  protected JSeparator getSeparator(SeparatorElement element) {
    JSeparator retcomponent = new JSeparator(JSeparator.VERTICAL);
    retcomponent.setVisible(true);
    return retcomponent;
  }
  
  protected JPanel getRadioButtonContainer(RadioButtonContainer element) {
    JPanel       retcomponent = new JPanel();
    ButtonGroup  buttonGroup  = new ButtonGroup();
    JRadioButton radioButton  = null;
    Border       border       = null;
    final int    elementmax   = element.size();
    
    if(element.isShowBorder() == true) {
      if(element.toString() == null || element.toString().length() == 0)
        border = BorderFactory.createLineBorder(Color.GRAY);
      else
        border = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.GRAY),
                                                  element.toString());
    }
    retcomponent.setBorder(border);
    
    for(int ielemcount = 0; ielemcount < elementmax; ielemcount ++) {
      radioButton = (JRadioButton)createElement(element.get(ielemcount));
      radioButton.putClientProperty(clientPropertyElementID, new Long(element.get(ielemcount).getElementID()));
      radioButton.setHorizontalTextPosition(JRadioButton.CENTER);
      radioButton.setVerticalTextPosition(JRadioButton.TOP);
      radioButton.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
      
      buttonGroup.add(radioButton);
      retcomponent.add(radioButton);
    }
    return retcomponent;
  }

  protected int getMaxWidthForTitleColumn(ElementContainer container) {
    final int contsize     = container.size();
          int icount       = 0;
          int maxsymbcount = 0;
    for(; icount < contsize; icount ++) {
      maxsymbcount = (maxsymbcount < ((ElementContainer)container.get(icount)).getTitleSize()) ?
                                        ((ElementContainer)container.get(icount)).getTitleSize() :
                                        maxsymbcount;
    }
    return getSymbolWidth() * maxsymbcount;
  }
  
  protected int getSymbolWidth() {
    Font        localfont = getFont();
    FontMetrics fm   = getFontMetrics((localfont == null) ? UIManager.getFont("Label.font") : localfont);
    return fm.charWidth('W');
  }
  
  protected int getSymbolHeight() {
   FontMetrics fm = getFontMetrics(getFont());
   return fm.getHeight();
  }

  public void updateElement(final Element element) {
	  
    switch(element.getElementType())
    {
     case Element.CHECKBOX_TYPE:
     {
       JCheckBox updateComp = (JCheckBox)getComponentByID(getContentPane().getComponents(), element.getElementID());
       updateComp.setSelected(((CheckBoxElement)element).getState());
     }
     break;
     case Element.RADIOBUTTON_TYPE:
     {
       JRadioButton updateComp = (JRadioButton)getComponentByID(getContentPane().getComponents(), element.getElementID());
       updateComp.setSelected(((RadioButtonElement)element).getState());
     }
     break;
     case Element.TEXTEDIT_TYPE:
     {
       JScrollPane scrollPane = (JScrollPane)getComponentByID(getContentPane().getComponents(), element.getElementID());
       JViewport   viewPort   = scrollPane.getViewport();
       final JTextArea textComp = (JTextArea)viewPort.getView();
       SwingUtilities.invokeLater(new Runnable() {
         public void run() {
           textComp.setText(((TextElement)element).getText());
         }
       });
     }
     break;
     case Element.TEXTLABEL_TYPE:
     {
       JLabel updateComp = (JLabel)getComponentByID(getContentPane().getComponents(), element.getElementID());
       if(updateComp != null) {
         updateComp.setText(((LabelElement)element).getText());
       }
     }
     break;
     default:
     break;
    }
  }
  
  protected JComponent getComponentByID(Component[] components, long compID) {
    JComponent  localComponent;
    JComponent  retComponent   = null;;
    Long        elementID;
    
    for(Component comp: components) {
      if(comp instanceof JComponent) {
        localComponent = (JComponent)comp;
        elementID  = (Long)localComponent.getClientProperty(clientPropertyElementID);
        if(elementID != null && elementID.longValue() == compID) {
          retComponent = localComponent;
          break;
        } else {
         retComponent = getComponentByID(((JComponent)comp).getComponents(), compID);
         if(retComponent != null)
          break;
        }
      }
    }
      
    return retComponent;
  }
  
  public class OKButtonActionListener implements ActionListener {
    public void actionPerformed(ActionEvent e)  {
        OKAction(e);
    }
  }
	  
  public class CancelButtonActionListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      closeAction(e);
    }
  }
	  
  protected void closeAction(ActionEvent e) {
    setVisible(false);
    dispose();
  }
  
  protected void OKAction(ActionEvent e) {
    closeAction(e);
  }
  
  public void actionPerformed(ActionEvent e) {
    JComponent jc   = (JComponent)e.getSource();
    Long elementID  = (Long)jc.getClientProperty(clientPropertyElementID);
    Element element = tabContainerModel.getByID(elementID.longValue());
    
    switch(element.getElementType()) {
      case Element.CHECKBOX_TYPE: {
        JCheckBox jelement = (JCheckBox)e.getSource();
        ((CheckBoxElement)element).setState(jelement.isSelected());
      }
      break;
      case Element.RADIOBUTTON_TYPE: {
        AbstractButton jrbelement           = (AbstractButton)e.getSource();
        DefaultButtonModel model            = (DefaultButtonModel)jrbelement.getModel();
        Enumeration<AbstractButton> enumval = model.getGroup().getElements();
        RadioButtonElement rbelement        = null;
        while(enumval.hasMoreElements()) {
          jrbelement   = (AbstractButton)enumval.nextElement();
          elementID  = (Long)jrbelement.getClientProperty(clientPropertyElementID);
          rbelement  = (RadioButtonElement)tabContainerModel.getByID(elementID.longValue());
          rbelement.setState(jrbelement.isSelected());
        }
      }
      break;
      case Element.TEXTEDIT_TYPE: {
        JTextArea jelement = (JTextArea)e.getSource();
        ((TextElement)element).setText(jelement.getText());
      } 
      break;
    }
    
    if(elementListener != null)
      elementListener.elementActionPerformed(element, this);
  }
  
  public void insertUpdate(DocumentEvent e) {
    documentChanged(e);
  }
  
  public void removeUpdate(DocumentEvent e) {
    documentChanged(e);
  }

  public void changedUpdate(DocumentEvent e) {
    documentChanged(e);
  }

  protected void documentChanged(DocumentEvent e) {
    String data = null;
    Long elementID = (Long)e.getDocument().getProperty(clientPropertyElementID);
    Element element = tabContainerModel.getByID(elementID.longValue());
    try {
     data = e.getDocument().getText(0, e.getDocument().getLength());
    } catch(BadLocationException unused) {};

    ((TextElement)element).setText(data);

    if(elementListener != null)
        elementListener.elementActionPerformed(element, this);
  }
  
  public void elementUpdatePerformed(Element e) {
    updateElement(e);
  }
}